#!/usr/bin/env bash

AMVERSION="7.4"

# Determine main repository and branch
AMREPO="https://raw.githubusercontent.com/ivan-hc/AM/main"
AMBRANCH=$(basename "$AMREPO")

# Determine the name of this script and its working directory
export REALDIR="$PWD"
DIR="$( cd "$( dirname "$0" )" && pwd )"
CLI=$(basename "$0")

# Determine system architecture and current user
arch="$HOSTTYPE"
export currentuser="$USER"

# XDG Variables
BINDIR="${XDG_BIN_HOME:-$HOME/.local/bin}"
DATADIR="${XDG_DATA_HOME:-$HOME/.local/share}"
CONFIGDIR="${XDG_CONFIG_HOME:-$HOME/.config}"
export CACHEDIR="${XDG_CACHE_HOME:-$HOME/.cache}"

function _create_cache_dir() {
	AMCACHEDIR="$AMPATH/.cache"
	mkdir -p "$AMCACHEDIR"
}

function _clean_amcachedir() {
	if [ -z "$AMCACHEDIR" ]; then exit 1; fi
	rm -f "$AMCACHEDIR"/*
}

APPMANCONFIG="$CONFIGDIR/appman"
export SCRIPTDIR="$(xdg-user-dir DESKTOP 2>/dev/null || echo "$HOME")"

# Colors
RED='\033[0;31m'; LightBlue='\033[1;34m'; Green='\033[0;32m'

# Prevent the use of "sudo" ("AM")
function _no_sudo() {
	if [ -n "$SUDO_COMMAND" ]; then
		printf '\n Please do not use "sudo" to execute "'"$CLI"'", try again.\n\n'
		exit 1
	fi
}

_no_sudo

#####################
# "AM" CORE VARIABLES
#####################

function _am() {
	AMCLI="am"
	AMCLIPATH="$AMCLI"
	SUDOCOMMAND="sudo"
	COMPLETIONPATH="/etc/bash_completion.d"
	COMPLETIONFILE="am-completion.sh"
	APPSPATH="/opt"
	AMPATH="$APPSPATH/$AMCLI"
	_create_cache_dir
	mkdir -p "$AMPATH"/modules
}

#######################################
# "APPMAN" CORE VARIABLES AND FUNCTIONS
#######################################

function _check_if_home_local_bin_is_not_in_path() {
	if ! echo "$PATH" | grep -q "$BINDIR"; then
		echo "--------------------------------------------------------------------------"
		echo " ⚠️ WARNING: \"${XDG_BIN_HOME:-$HOME/.local/bin}\" is not in PATH, apps may not run."
		read -rp " ◆ Want to add it to ~/.profile and ~/.bash_profile? (Y/n): " yn
		if ! echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
			if test -f "$HOME/.profile"; then
				printf '\n%s\n' 'export PATH="$PATH:${XDG_BIN_HOME:-$HOME/.local/bin}"' >> "$HOME/.profile"
			elif test -f "$HOME/.bash_profile"; then
				printf '\n%s\n' 'export PATH="$PATH:${XDG_BIN_HOME:-$HOME/.local/bin}"' >> "$HOME/.bash_profile"
			fi
			echo "--------------------------------------------------------------------------"
			echo " You might need to resource above files or reboot to apply these changes."
			echo "--------------------------------------------------------------------------"
		fi
	fi
}

function _appman_initialize() {
	APPSDIR=$1
	mkdir -p ~/"$APPSDIR"/appman/modules
	mkdir -p "$BINDIR" "$DATADIR"/applications "$DATADIR"/icons
	AMCLI="appman"
	AMCLIPATH="$DIR/$AMCLI"
	SUDOCOMMAND=""
	COMPLETIONPATH="$HOME"
	COMPLETIONFILE=".bash_completion"
	APPSPATH="$HOME/$APPSDIR"
	AMPATH="$HOME/$APPSDIR/$AMCLI"
	_create_cache_dir
}

function _appman_prompt_for_directory() {
	local location
	read -r -ep "Please, write the name or the path of your custom application's folder: $(echo -e '\n\n '"$HOME"'/')" location
	echo "$location"
}

function _appman_validate_directory() {
	local location=$1
	if [[ "$location" == "" ]]; then
		echo -e "--------------------------------------------------------------------------"
		echo "OPERATION ABORTED!"
		exit  1
	elif [[ "$location" == ".local/bin" ]]; then
		echo -e "--------------------------------------------------------------------------\n"
		echo -e " 💀 ERROR, you can't install applications into a '$PATH'\n\n The directory ~/.local/bin and 'bin' folders are used for executables.\n\n Please, choose a different path and retry! \n"
		echo -e "--------------------------------------------------------------------------\n"
		exit  1
	else
		local location_appman="${location/#$HOME}"
		location_appman="${location_appman// /-}"
		location_appman="${location_appman#/}"
		location_appman="${location_appman%/}"
		echo "$location_appman"
	fi
}

function _appman() {
	if [[ ! -f "$APPMANCONFIG"/appman-config ]]; then
		echo "--------------------------------------------------------------------------"
		echo " >>> Thank you for choosing AppMan!"
		echo "--------------------------------------------------------------------------"
		echo "Before proceeding with any task, write the name of the directory in which"
		echo 'you will install the apps, for example "Programs" or "My-apps", you can '
		echo 'also choose a subfolder, for example ".local/My-apps" or a deeper path.'
		echo -e "\nThe destination folder will be created in $HOME"
		echo -e "\nSYNTAX: PATH/TO/DIRNAME\n"
		echo 'NOTE, any spaces or "/" at the beginning and end will be removed. If you '
		echo 'decide to change your choice in the future, first remove all the programs'
		echo 'and then edit the ~/.config/appman/appman-config text file.'
		echo -e "--------------------------------------------------------------------------\n"
		local location=$(_appman_prompt_for_directory)
		local validated_location=$(_appman_validate_directory "$location")
		mkdir -p "$APPMANCONFIG"
		echo "$validated_location" >> "$APPMANCONFIG"/appman-config
		echo '--------------------------------------------------------------------------'
		echo 'You are ready! Start installing your favorite apps locally!!'
		echo "All apps will be installed in $HOME/$validated_location"
		echo -e '\nIn case of problems, use the option "-h".'
		echo '--------------------------------------------------------------------------'
	fi
	_appman_initialize "$(<"$APPMANCONFIG"/appman-config)"

	_check_if_home_local_bin_is_not_in_path
}

########################################
# DETERMINE WHEN TO USE "AM" OR "APPMAN"
########################################

if [[ "$DIR/$CLI" == "/usr/local/bin/am" ]] 2>/dev/null; then
	_am
else
	_appman
fi

# Set header authorization if GitHub API key file exists
[ -f "$AMPATH/ghapikey.txt" ] && HeaderAuthWithGITPAT=" --header \"Authorization: token $(<"$AMPATH/ghapikey.txt")\" "

################
# DEVELOPER MODE
################

if test -f "$AMPATH"/betatester; then
	AMREPO="https://raw.githubusercontent.com/ivan-hc/AM/dev"
	AMBRANCH=$(basename "$AMREPO")
fi
function _betatester_message_on() {
	if test -f "$AMPATH"/betatester; then
		echo '--------------------------------------------------------------------------'
		echo "\"$AMCLI\" $AMVERSION: developer mode" | tr '[:lower:]' '[:upper:]'
		echo '--------------------------------------------------------------------------'
	fi
}

function _use_betatester_on() {
	touch "$AMPATH"/betatester
}

function _use_betatester_off() {
	rm -f "$AMPATH"/betatester
}

###########################################
# FUNCTIONS RELATED TO THE SAFE USE OF "AM"
###########################################

function _am_dependences_check() {
	# Check for essential commands required by the application
	missing_deps=()
	AMDEPENDENCES="cat chmod chown curl grep sed wget"
	for name in $AMDEPENDENCES; do
		if ! command -v "$name" &>/dev/null; then
			missing_deps+=("$name")
		fi
	done

	# Exit if any essential command is missing
	if [ -n "$missing_deps" ]; then
		echo '--------------------------------------------------------------------------'
		printf " ${RED}💀 ERROR! MISSING ESSENTIAL COMMANDS\033[0m: %s\n\n Install the above and try again! \n" "${missing_deps[*]}"
		echo '--------------------------------------------------------------------------'
		echo -e "\n ${Green}List of the $(echo "$AMCLI" | tr '[:lower:]' '[:upper:]') $AMVERSION core dependences\033[0m:\n"
		echo "$AMDEPENDENCES" | tr ' ' ',' | sed 's/,/, /g' | sed 's/^/ /g'
		echo -e '\n--------------------------------------------------------------------------'
		echo " If this message appears it is because you are missing some dependency"
		echo " and if its the first time its because something new has been introduced."
		echo -e "\n See ${LightBlue}https://github.com/ivan-hc/AM#dependences\033[0m for more information"
		echo '--------------------------------------------------------------------------'
		exit 1
	fi
}

function _am_security_check() {
	_am_dependences_check
	# Check and create the $AMPATH directory if it does not exist
	if [ ! -d "$AMPATH" ]; then
		$SUDOCOMMAND mkdir -p "$AMPATH"
	fi
}

# Function to check online connections (uses github.com by default, as the database and CLI itself are stored/hosted there)
function _online_check() {
	if ! wget -q --tries=10 --timeout=20 --spider https://github.com; then
		echo -e "\n $AMCLI is offline, please check your internet connection and try again\n"
		exit
	fi
}

###########################################
# FUNCTIONS RELATED TO THE DATABASE OF APPS
###########################################

function _am_newrepo_check() {
	# Determine if the CLI uses the "main" branch of https://github.com/ivan-hc/AM or an alternative one
	if [[ -f "$AMPATH"/new-repo.txt ]]; then
		AMREPO=$(<"$AMPATH/new-repo.txt") # Utilizing command substitution with < for more efficient file reading
		AMBRANCH=$(basename "$AMREPO")
		export APPSDB="$AMREPO/programs/$arch"
		export APPSLISTDB="$AMREPO/programs/$arch-apps"
		export LIBSLISTDB="$AMREPO/programs/libs-list"
		if [[ "$*" != "newrepo off" ]]; then
			echo '--------------------------------------------------------------------------'
			echo " Source: $AMREPO"
			echo '--------------------------------------------------------------------------'
		fi
	else
		export APPSDB="$AMREPO/programs/$arch"
		export APPSLISTDB="$AMREPO/programs/$arch-apps"
		export LIBSLISTDB="$AMREPO/programs/libs-list"
	fi
}

function _am_databases_check() {
	# Determine catalogue in use
	export AMCATALOGUEMARKDOWNS="https://raw.githubusercontent.com/Portable-Linux-Apps/Portable-Linux-Apps.github.io/main/apps"
	export AMCATALOGUEICONS="https://raw.githubusercontent.com/Portable-Linux-Apps/Portable-Linux-Apps.github.io/main/icons"
	_am_newrepo_check
	# To implement future application database changes
	if [ -f "$AMPATH/neodb" ]; then
		if grep -q "://" "$AMPATH/neodb"; then
			if grep -q "#STATUS=quiet" "$AMPATH/neodb"; then
				case "$1" in
				'-a'|'about'|'-d'|'download'|'-i'|'install'|'-l'|'list'|'-q'|'query')
					echo '--------------------------------------------------------------------------'
					echo " Source: Third party repositories are enabled"
					echo '--------------------------------------------------------------------------'
					;;
				esac
			fi
		fi
	fi
}

#####################################
# CHECK VERSION OF THE INSTALLED APPS
#####################################

function _check_version_if_any_version_reference_is_somewhere() {
	APPVERSION=$(grep -i "version=" "$APPSPATH"/"$arg"/* 2>/dev/null | grep -Eo "([0-9]{1,}\.)+[0-9]{1,}" | head -1)
}

function _check_version_if_zsync_file_exists() {
	APPVERSION=$(strings -d ./"$arg"/*.zsync | grep -i "$(echo "$arg" | sed 's/-appimage//g')" | grep -Eo "([0-9]{1,}\.)+[0-9]{1,}" | head -1)
	if [ -z "$APPVERSION" ]; then
		APPVERSION=$(date -r ./"$arg"/*.zsync "+%Y.%m.%d")
	fi
}

function _check_version_if_version_file_exists() {
	APPVERSION=$(cat "$APPSPATH"/"$arg"/version | head -1 | sed 's:.*/::' \
		| sed "s/$arch//g; s/amd64//g; s/x86-64//g; s/x64//g; s/basic//g; s/standard//g; s/full//g; s/help//g;" \
		| tr '-' '\n' | tr '_' '\n' | grep -vi "appimage\|$arg\|?\|tar." | grep "[0-9]" | head -1 | sed 's/^v//g; s/^\.//g; s/\.$//g;')
	if [ -z "$APPVERSION" ]; then
		if grep -q "download$" ./"$arg"/version; then
			APPVERSION=$(cat "$APPSPATH"/"$arg"/version | tr '/' '\n' \
				| sed "s/$arch//g; s/amd64//g; s/x86-64//g; s/x64//g; s/basic//g; s/standard//g; s/full//g; s/help//g;" \
				| tr '_' '\n' | grep -vi "appimage\|$arg\|?\|tar." | grep "[0-9]" | tail -1 | sed 's/^v//g; s/^\.//g; s/\.$//g;')
		elif grep -q "://" ./"$arg"/version; then
			APPVERSION=$(cat ./"$arg"/version | tr '/' '\n' | grep -Eo "([0-9]{1,}\.)+[0-9]{1,}" | head -1)
		elif grep -q "/v[0-9]*" ./"$arg"/version; then
			APPVERSION=$(cat "$APPSPATH"/"$arg"/version | tr '/' '\n' | grep "^v[0-9]" | head -1 | sed 's/^v//g')
		elif [ "$(cat ./"$arg"/version | wc -w)" == 1 ]; then
			APPVERSION=$(cat ./"$arg"/version | head -1)
		fi
	fi
}

function _check_version_if_an_updater_exists() {
	APPVERSION=$("$APPSPATH"/"$arg"/updater -d "$APPSPATH"/"$arg"/"$arg" 2>/dev/null | grep -i "$arg" | grep -Eo "([0-9]{1,}\.)+[0-9]{1,}" | head -1)
	if [ -z "$APPVERSION" ]; then
		_check_version_if_any_version_reference_is_somewhere
	fi
}

function _check_version_if_amcli() {
	APPVERSION="$AMVERSION"
}

function _check_version_if_firefox_webapp() {
	APPVERSION="WebApp"
}

function _check_version_if_library() {
	APPVERSION=$(find $(cat "$APPSPATH"/"$arg"/remove | tr ' ' '\n' | grep "usr/local/lib") -type f | sed 's:.*.so.::' | tail -1)
}

function _check_version_if_no_version_found() {
	APPVERSION="unknown"
}

function _check_version() {
	rm -f "$AMCACHEDIR"/version-args
	cd "$APPSPATH" &&
	INSTALLED_APPS=$(find -name 'remove' -printf "%h\n" 2>/dev/null | du -sh -- * 2> /dev/null | sort -rh | sed 's@.*	@@')
	for arg in $INSTALLED_APPS; do
		if test -f ./"$arg"/remove 2>/dev/null; then
			if test -f ./"$arg"/*.zsync 2>/dev/null; then
				_check_version_if_zsync_file_exists
			elif test -f ./"$arg"/version 2>/dev/null; then
				_check_version_if_version_file_exists
			elif test -f ./"$arg"/updater 2>/dev/null; then
				_check_version_if_an_updater_exists
			elif [ "$arg" == "$AMCLI" ]; then
				_check_version_if_amcli
			elif grep -qi "version=" ./"$arg"/* 2>/dev/null; then
				_check_version_if_any_version_reference_is_somewhere
			elif echo "$arg" | grep -q "ffwa-"; then
				_check_version_if_firefox_webapp
			elif cat "$APPSPATH"/"$arg"/remove 2> /dev/null | grep -q "usr/local/lib"; then
				_check_version_if_library
			else
				_check_version_if_no_version_found
			fi
			if [ -z "$APPVERSION" ]; then
				_check_version_if_no_version_found
			fi
			echo " ◆ $arg	|	$APPVERSION" >> "$AMCACHEDIR"/version-args
		fi
	done
}

function _check_version_for_auto_updatable_apps() {
	cd "$APPSPATH" &&
	INSTALLED_APPS=$(find -name 'remove' -printf "%h\n" 2>/dev/null | du -sh -- * 2> /dev/null | sort -rh | sed 's@.*	@@')
	for arg in $INSTALLED_APPS; do
		if test -f ./"$arg"/updater 2>/dev/null; then
			if [ -z "$(strings -d "./$arg/$arg" 2>/dev/null | grep -F 'appimage')" ] 2>/dev/null; then
				_check_version_if_an_updater_exists
				OLDAPPVERSION=$(cat "$AMCACHEDIR"/version-args | grep " ◆ $arg	|" | tr '|' '\n' | sed 's/	//g' | head -2 | tail -1)
				sed -i "/ ◆ $arg	|/s#$OLDAPPVERSION#$APPVERSION#" "$AMCACHEDIR"/*
			fi
		fi
	done
}

if test -f "$AMCACHEDIR"/version-args; then
	_check_version_for_auto_updatable_apps 2>/dev/null
fi

function _remove_info_files() {
	rm -f "$AMCACHEDIR"/files*
	rm -f "$AMCACHEDIR"/version-args
}

###################################################################################
# FUNCTIONS RELATED TO INTERNAL OPTIONS TO ENABLE/DISABLE BASH (AND ZSH) COMPLETION
###################################################################################

function _use_disable-completion() {
	if [ "$(grep -o "$AMCLI" 0<"$COMPLETIONPATH/$COMPLETIONFILE" | head -1)" == "$AMCLI" ]; then
		grep -v 'complete -W "$(cat '"$AMPATH"'/list 2>/dev/null)" '"$AMCLI"'' "$COMPLETIONPATH/$COMPLETIONFILE" > "$AMPATH"/$COMPLETIONFILE.backup &&
		$SUDOCOMMAND mv "$AMPATH"/$COMPLETIONFILE.backup "$COMPLETIONPATH/$COMPLETIONFILE"
		echo " Completion disabled! "
	else
		echo " Completion script is NOT enabled! "
	fi
}

function _use_enable_completion() {
	if [ "$(grep -o "$AMCLI" 0<"$COMPLETIONPATH/$COMPLETIONFILE" | head -1)" == "$AMCLI" ]; then
		echo " Completion script is already enabled!"
	else
		$SUDOCOMMAND mkdir -p "$COMPLETIONPATH"
		if [ "$AMCLI" == appman ]; then
			if ! test -f "$COMPLETIONPATH/$COMPLETIONFILE"; then
				echo '#!/usr/bin/env bash' >> "$COMPLETIONFILE"
			fi
		else
			echo '#!/usr/bin/env bash' >> "$COMPLETIONFILE"
		fi
		echo 'complete -W "$(cat '"$AMPATH"'/list 2>/dev/null)" '"$AMCLI"'' >> "$COMPLETIONFILE"
		chmod a+x "$COMPLETIONFILE"
		$SUDOCOMMAND mv "$COMPLETIONFILE" "$COMPLETIONPATH/" 2> /dev/null
		echo " Completion has been enabled!"
	fi
	if [ -f "$HOME/.zshrc" ] || [ -f "$ZDOTDIR/.zshrc" ]; then
		if [ -n "$ZDOTDIR" ]; then
			ZSHDIR="$ZDOTDIR"
		else
			ZSHDIR="$HOME"
		fi
		if grep -q "source $ZSHDIR/$COMPLETIONFILE" "$ZSHDIR/.zshrc"; then
			echo "Zsh completion already enabled!"
		else
			echo '#!/usr/bin/env bash' >> "$ZSHDIR/$COMPLETIONFILE"
			echo 'complete -W "$(cat '"$AMPATH"'/list 2>/dev/null)" '"$AMCLI"'' >> "$ZSHDIR/$COMPLETIONFILE"
			chmod a+x "$ZSHDIR/$COMPLETIONFILE"
			echo -e "\nautoload bashcompinit\nbashcompinit\nsource $ZSHDIR/$COMPLETIONFILE" >> "$ZSHDIR/.zshrc"
		fi
	fi
}

# Generate a list of words to use in bash completion
function _generate_options_list() {
	rm -f "$AMPATH/options"
	cat <<-HEREDOC >> "$AMPATH/options"
		about
		apikey
		backup
		clean
		downgrade
		download
		extra
		files
		install
		list
		lock
		neodb
		newrepo
		nolibfuse
		overwrite
		query
		remove
		sync
		template
		test
		unlock
		update
		web
		--apps
		--byname
		--config
		--convert
		--debug
		--devmode-disable
		--devmode-enable
		--disable-completion
		--enable-completion
		--force-latest
		--home
		--launcher
		--less
		--pkg
		--rollback
		--disable-sandbox
		--sandbox
		--silent
		--system
		--user
	HEREDOC
}

function _completion_lists() {
	_generate_options_list
	# Remove existing lists and download new ones
	rm -Rf "$AMPATH"/{"$arch"-apps,list,libs-list} &&
	wget -q "$APPSLISTDB" -P "$AMPATH" &&
	wget -q "$LIBSLISTDB" -P "$AMPATH"

	# THIRD PARTY APPS
	if test -f "$AMPATH/neodb"; then
		rm -R -f "$AMCACHEDIR/multirepo-args"
		MULTIREPO=$(cat "$AMPATH/neodb" | grep "List=" | grep -E "$arch-apps" | sed 's/List=//g')
		for anyrepo in $MULTIREPO; do
			if curl --output /dev/null --silent --head --fail "$anyrepo" 1>/dev/null; then
				wget -q "$anyrepo" -O - | grep "◆" >> "$AMPATH"/"$arch"-apps
			fi
		done
		cat "$AMPATH"/"$arch"-apps | sort | uniq > "$AMPATH"/"$arch"-apps-backup
		rm -R -f "$AMPATH"/"$arch"-apps
		mv "$AMPATH"/"$arch"-apps-backup "$AMPATH"/"$arch"-apps
	fi

	# Extract and append program and library names to the list
	for file in "$arch"-apps libs-list; do
		awk -v FS="(◆ | : )" '{print $2}' <$AMPATH/$file >> "$AMPATH"/list
	done

	# Append options to the list
	cat "$AMPATH"/options >> "$AMPATH"/list
}

#####################################################################
# FUNCTIONS RELATED TO INTERNAL OPTIONS TO MADE "AM" WORK AS "APPMAN"
#####################################################################

# Function for the option "appman" or "--user" that helps "AM" function like "AppMan"
function _appman_mode_enabled_message() {
	if [ "$CLI" == am ] 2>/dev/null; then
		echo '--------------------------------------------------------------------------'
		#echo " \"AppMan Mode\" enabled!" | tr '[:lower:]' '[:upper:]'
		echo -e " \"AM\" is running as \"AppMan\", use ${Green}am --system\033[0m to switch it back to \"AM\""
		echo '--------------------------------------------------------------------------'
	fi
}

function _if_appman_mode_enabled() {
	if [ "$CLI" == am ]; then
		if test -f "$APPMANCONFIG"/appman-mode; then
			_appman_mode_enabled_message
		fi
	fi
}

function _use_appman() {
	_online_check
	if [ "$CLI" == appman ] 2>/dev/null; then
		echo " This function only works for AM"
		exit
	fi
	if test -f "$APPMANCONFIG"/appman-mode; then
		_appman
		_appman_mode_enabled_message
	else
		mkdir -p "$APPMANCONFIG"
		touch "$APPMANCONFIG"/appman-mode
		_appman
		_appman_mode_enabled_message
	fi
}

function _back_to_am() {
	if test -f "$APPMANCONFIG"/appman-mode; then
		rm -f "$APPMANCONFIG"/appman-mode
		echo '--------------------------------------------------------------------------'
		echo " \"AppMan Mode\" disabled!" | tr '[:lower:]' '[:upper:]'
		echo '--------------------------------------------------------------------------'
	fi
}

if [ "$AMCLI" == am ] 2>/dev/null; then
	if test -f "$APPMANCONFIG"/appman-mode; then
		case "$1" in
		'--system')
			_back_to_am
			;;
		''|*)
			if ! test -f "$APPMANCONFIG"/appman-config; then
				_appman_mode_enabled_message
			fi
			_appman
			AMCLIPATH="/opt/am/APP-MANAGER"
			;;
		esac
	elif [ ! -w /opt/am ]; then
		read -p " \"AM\" is read-only, want to use it in \"AppMan Mode\" (Y,n)? " yn
		case $yn in
		'N'|'n')
			exit 0;;
		'Y'|'y'|*)
			echo '--------------------------------------------------------------------------'
			echo " \"AppMan Mode\" enabled!" | tr '[:lower:]' '[:upper:]'
			echo '--------------------------------------------------------------------------'
			_use_appman 1>/dev/null;;
		esac
	fi
fi

##############################################################
# FUNCTIONS RELATED TO INTERNAL OPTION TO CLEAN UNNEEDED FILES
##############################################################

function _clean_amcachedir_message() {
	_clean_amcachedir
	echo " ◆ Clear the contents of $AMCACHEDIR"
}

function _clean_all_home_cache_directories_of_appimages() {
	if test -d "$APPSPATH"/*/*.home/.cache 2> /dev/null; then
		rm -Rf "$APPSPATH"/*/*.home/.cache/* &&
		echo " ◆ Clear the contents of all *.home/.cache AppImages directories"
	fi
}

function _clean_all_tmp_directories_from_appspath() {
	if test -d "$APPSPATH"/*/tmp 2> /dev/null; then
		rm -Rf "$APPSPATH"/*/tmp &&
		echo ' ◆ Removed all '"$APPSPATH"'/*/tmp directories'
	fi
}

function _clean_launchers() {
	if test -d "$DATADIR"/applications/AppImages 2> /dev/null; then
		for var in "$DATADIR"/applications/AppImages/*.desktop; do
			APPIMAGENAME=$(grep "Exec=" 0<"$var" 2>/dev/null | head -1 | cut -c 6- | sed 's/\s.*$//')
			MOUNTPOINTS=$(echo "$APPIMAGENAME" | cut -d'/' -f1-4)
			if ! test -f "$APPIMAGENAME" 2> /dev/null; then
				if ! test -d "$(echo "$MOUNTPOINTS")" 2> /dev/null; then
					if echo "$MOUNTPOINTS" | grep -q "/media/"; then
						echo -e "\n 💀ERROR: cannot remove $(basename "$var")\n   related AppImage is located in an unmounted path of /media\n"
					elif echo "$MOUNTPOINTS" | grep -q "/mnt/"; then
						echo -e "\n 💀ERROR: cannot remove $(basename "$var")\n   related AppImage is located in an unmounted path of /mnt\n"
					fi
				else
					rm -f "$var"
					rm -f "$HOME"/.local/bin/"$(basename -- "$(echo "$APPIMAGENAME" | tr '[:upper:]' '[:lower:]')")"*
					cd "$HOME"/.local/bin && find . -xtype l -delete
				fi
			fi
		done
		echo ' ◆ Removed orphaned launchers produced with the "--launcher" option'
		rmdir "$DATADIR"/applications/AppImages
	fi
}

function _clean_old_modules() {
	if [ "$AMCLI" == am ] 2>/dev/null; then
		MODULES=$(cat /opt/am/APP-MANAGER | grep -Eo '\S*.am\S*' | sed 's@.*/@@' | grep -v ";" | grep ".am" | uniq | sort)
	else
		MODULES=$(cat "$AMCLIPATH" 2>/dev/null | grep -Eo '\S*.am\S*' | sed 's@.*/@@' | grep -v ";" | grep ".am" | uniq | sort)
	fi
	for m in "$APPSPATH"/"$AMCLI"/modules/*; do
		if [[ "${MODULES}" != *"$(basename -- "$m")"* ]];then
			rm -f "$m" 2>/dev/null
			echo " ◆ Removed obsolete module named \"$(basename -- "$m")\""
		fi
	done
}

function _use_clean() {
	echo -e "\n Cleaning temporary files and folders...\n" && sleep 0.1 &&
	for i in {100..000}; do
		echo -ne " $i\r" && sleep 0.0001
	done
	_clean_amcachedir_message
	_clean_all_home_cache_directories_of_appimages
	_clean_all_tmp_directories_from_appspath
	_clean_launchers 2> /dev/null
	_clean_old_modules
	echo -e "\n DONE!\n"
}

#########################################################################################
# FUNCTIONS RELATED TO INTERNAL OPTIONS TO UPDATE "AM"/"APPMAN", THE APPS AND THE MODULES
#########################################################################################

# Function to update GitHub API key in AM-updater files
function _update_github_api() {
	local ghapikey_file="$AMPATH/ghapikey.txt"
	if [[ -f "$ghapikey_file" ]]; then
		ghapikey=$(<"$ghapikey_file")
		local updater_files=("$APPSPATH"/*/AM-updater) # Assuming AM-updater is one level deeper
		for f in "${updater_files[@]}"; do
		if [[ -f "$f" ]] && grep -q "https://api.github.com" "$f"; then
			# Check if the file already contains a valid API key
			if ! grep -qE "(gh[ps]_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})" "$f"; then
				# Insert HeaderAuthWithGITPAT before the GitHub API URL
				sed -i "s#https://api.github.com#$HeaderAuthWithGITPAT https://api.github.com#g" "$f"
			else
				# Replace existing API key with the one from ghapikey.txt
				sed -i "s#\(gh[ps]_[a-zA-Z0-9]\{36\}\|github_pat_[a-zA-Z0-9]\{22\}_[a-zA-Z0-9]\{59\}\)#$ghapikey#g" "$f"
			fi
		fi
		done
    	fi
}

function _list_updatable_apps() {
	_check_version
	cd "$APPSPATH" && find -name 'AM-updater' -printf " %h\n" 2>/dev/null | sort -u | xargs -n 1 basename 2>/dev/null > "$AMCACHEDIR"/updatable-args
	ARGS=$(cat "$AMCACHEDIR"/updatable-args)
	for arg in $ARGS; do
		app_version=$(cat "$AMCACHEDIR/version-args" | grep -w " ◆ $arg	|" | sed 's:.*|	::')
		echo " ◆ $arg $app_version" >> "$AMCACHEDIR"/updatable-args-list
	done
}

function _sync_installation_scripts() {
	echo "-----------------------------------------------------------------------------"
	echo -e " Checking for changes of the installation scripts in the online database..."
	rm -R -f "$AMCACHEDIR"/sync-args && echo "" >> "$AMCACHEDIR"/sync-args
	cd "$APPSPATH" && find -name 'AM-updater' -printf "%h\n" 2>/dev/null | sort -u | cut -c 3- > "$AMCACHEDIR"/sync-args
	ARGS=$(cat "$AMCACHEDIR"/sync-args)
	for arg in $ARGS; do
		mkdir -p "$APPSPATH/$arg/.am-installer"
		if test -f "$APPSPATH/$arg/.am-installer/$arg"; then
			CURRENT=$(cat "$APPSPATH"/"$arg"/.am-installer/"$arg")
			SOURCE=$(wget -q "$APPSDB"/"$arg" -O -)
			if [ "$CURRENT" == "$SOURCE" ]; then
				echo -ne "\r" 2> /dev/null
			else
				echo -e " ◆ Changed https://github.com/ivan-hc/AM/blob/main/programs/$arch/$arg"
			fi
		else
			if curl --output /dev/null --silent --head --fail "$APPSDB"/"$arg"  1>/dev/null; then
				echo -e " ◆ No installation script for $arg, downloading one..."
				mkdir -p "$APPSPATH/$arg/.am-installer"
				wget -q "$APPSDB/$arg" -O "$APPSPATH/$arg/.am-installer/$arg"
			fi
		fi
	done
}

function _sync_modules() {
	echo "-----------------------------------------------------------------------------"
	echo -e " Check for updates in modules..."
	MODULES=$(curl -Ls $HeaderAuthWithGITPAT https://api.github.com/repos/ivan-hc/AM/contents/modules?ref="$AMBRANCH" | sed 's/[()",{}]/ /g; s/ /\n/g' | grep -o 'https.*raw.*modules.*am$' | grep -v "sync\|update")
	for v in $MODULES; do
		cd "$AMPATH"/modules || return
		MODULENAME=$(echo "$v" | sed 's:.*/::')
		if ! test -f ./"$MODULENAME"; then
			echo " ◆ Downloading $MODULENAME (not previously installed)..."
			wget -q "$v"
			chmod a+x ./"$MODULENAME"
		fi
		CURRENT=$(cat ./"$MODULENAME" 2>/dev/null)
		SOURCE=$(wget -q "$v" -O -)
		if [ "$CURRENT" == "$SOURCE" ]; then
			echo -ne "\r" 2> /dev/null
		else
			echo " ◆ Updating $MODULENAME..."
			mkdir "$AMCACHEDIR"/tmp
			cd "$AMCACHEDIR"/tmp || return
			wget -q "$v"
			cd ..
			echo y | mv tmp/*.am "$AMPATH"/modules/
			rmdir tmp
		fi
	done
	_clean_old_modules
}

function _sync_amcli() {
	echo "-----------------------------------------------------------------------------"
	_completion_lists
	rm -f /opt/am/appman
	if [ "$AMCLIPATH" == /opt/am/appman ] 2>/dev/null; then
		AMCLIPATH="/opt/am/APP-MANAGER"
		CURRENT_AM_VERSION=$("$AMCLIPATH" -v)
	else
		CURRENT_AM_VERSION=$("$AMCLIPATH" -v 2>/dev/null)
	fi
	echo -ne '\n ◆ SYNCHRONIZING "'"$(echo "$AMCLI" | tr '[:lower:]' '[:upper:]')"'" VERSION '"$CURRENT_AM_VERSION"'...\r'; sleep 1
	_clean_amcachedir 1>/dev/null; cd "$AMCACHEDIR" || return
	if [ "$AMCLI" == am ] 2>/dev/null; then
		wget -q "$AMREPO"/APP-MANAGER && chmod a+x ./APP-MANAGER
		cd ..
		echo y | mv "$AMCACHEDIR"/APP-MANAGER /opt/am/APP-MANAGER
	else
		wget -q "$AMREPO"/APP-MANAGER -O appman && chmod a+x ./"$AMCLI"
		cd ..
		echo y | mv "$AMCACHEDIR"/"$AMCLI" "$AMCLIPATH" 2>/dev/null
	fi
	if [ ! "$CURRENT_AM_VERSION" == "$("$AMCLIPATH" -v 2>/dev/null)" ] ; then
		echo -ne ' A new release of "'"$(echo "$AMCLI" | tr '[:lower:]' '[:upper:]')"'" is available, please wait...\r'
		echo -e ' ◆ "'"$(echo "$AMCLI" | tr '[:lower:]' '[:upper:]')"'" IS NOW UPDATED TO THE BRAND NEW '"$("$AMCLIPATH" -v)"' VERSION!           \n\n  Replacement of version '"$CURRENT_AM_VERSION"' currently in use, COMPLETED!'
		echo -e "\n See https://github.com/ivan-hc/AM/commits/main\n"
	else
		echo -e ' ◆ "'"$(echo "$AMCLI" | tr '[:lower:]' '[:upper:]')"'" IS ALREADY UPDATED, CURRENT VERSION '"$CURRENT_AM_VERSION"''
		echo -e "\n See https://github.com/ivan-hc/AM/commits/$AMBRANCH\n"
	fi
}

function _use_sync() {
	if [[ "$AMREPO" = *://* ]]; then
		_sync_installation_scripts
		_sync_modules
		_sync_amcli
	fi
}

function _update_all_apps() {
	for f in "$APPSPATH"/*/; do
		cd "$f" 2>/dev/null &&
		if test -f ./AM-updater; then
			if [ -w ./AM-updater ]; then
				APPNAME=$(echo "$(printf '%s\n' "${PWD##*/}")")
				start=$(date +%s) && sh -x ./AM-updater > /dev/null 2>&1 | echo -ne ' Updating "'"$APPNAME"'"...\r' && end=$(date +%s) &&
				echo -e " ◆ $(echo "$APPNAME" | tr '[:lower:]' '[:upper:]') is updated, $(($end-$start)) seconds elapsed!" &
			else
				APPNAME=$(echo "$(printf '%s\n' "${PWD##*/}")")
				echo -e " - $(echo "$APPNAME" | tr '[:lower:]' '[:upper:]') is read-only, cannot update it!"
			fi
		else
			echo ""  > /dev/null 2>&1
		fi
	done
	wait
	echo '-----------------------------------------------------------------------------'
	if test -f "$AMCACHEDIR"/updatable-args-list; then
		mv "$AMCACHEDIR"/updatable-args-list "$AMCACHEDIR"/updatable-args-list-old
		_list_updatable_apps
		OLDVER="$AMCACHEDIR/updatable-args-list-old"
		NEWVER="$AMCACHEDIR/updatable-args-list"
		if cmp --silent -- "$NEWVER" "$OLDVER"; then
			echo ' Nothing to do here!'
		else
			echo -e " The following apps have been updated:\n"
			diff --new-line-format="" --unchanged-line-format="" "$NEWVER" "$OLDVER"
			echo ""
		fi
	else
		echo ' No apps to update here!'
	fi
	rm -R -f "$APPSPATH/*/tmp"
}

function _use_update() {
	_update_github_api
	while [ -n "$1" ]; do
		case $2 in
		''|'--apps')
			_clean_amcachedir
			_list_updatable_apps
			echo '-----------------------------------------------------------------------------'
			echo -e ' "'"$(echo "$AMCLI" | tr '[:lower:]' '[:upper:]')"'" CAN MANAGE UPDATES FOR THE FOLLOWING PROGRAMS:\n'
			if test -f "$AMCACHEDIR/updatable-args-list"; then
				cat "$AMCACHEDIR/updatable-args-list"
			else
				echo " None"
			fi
			echo -e '\n All self-updatable programs are excluded'; sleep 0.1
			echo '-----------------------------------------------------------------------------'
			echo ' >> START OF ALL PROCESSES <<'
			echo '-----------------------------------------------------------------------------'

			_update_all_apps

			if [ -z "$2" ]; then
				echo "-----------------------------------------------------------------------------"
				_use_sync
			fi
			echo '-----------------------------------------------------------------------------'
			echo ' >> END OF ALL PROCESSES << '
			echo '-----------------------------------------------------------------------------'
			sleep 0.2
			exit;;
		*)
			if test -f "$APPSPATH"/"$2"/AM-updater; then
				if [ -w "$APPSPATH"/"$2"/AM-updater ]; then
					start=$(date +%s); "$APPSPATH"/"$2"/AM-updater > /dev/null 2>&1 | echo -ne " UPDATING $(echo "$2" | tr '[:lower:]' '[:upper:]')\r"; end=$(date +%s)
					echo -e " ◆ $(echo "$2" | tr '[:lower:]' '[:upper:]') is updated, $(($end-$start)) seconds elapsed!"
					_clean_amcachedir
					_list_updatable_apps
					break
				else
					echo -e " - $(echo "$2" | tr '[:lower:]' '[:upper:]') is read-only, cannot update it!"
					exit
				fi
			else
				echo ' "'"$(echo "$AMCLI" | tr '[:lower:]' '[:upper:]')"'" CANNOT MANAGE UPDATES FOR "'"$(echo "$2" | tr '[:lower:]' '[:upper:]')"'"'
				UPDATERS=$(cd "$APPSPATH"/"$2" 2>/dev/null && find . -name "*update*" -print 2>/dev/null)
				if [ -n "$UPDATERS" ]; then
					echo ' This program probably includes its own update system!'
				fi
				exit
			fi;;
		esac
	done
	shift
}

function _use_force_latest() {
  	case $2 in
	'')
		echo " USAGE: $AMCLI $1 [ARGUMENT]"; exit
		;;
	*)
		if ! test -d "$APPSPATH"/"$2"; then
			echo " ERROR: \"$2\" is not installed, see \"-f\""
		elif ! test -f "$APPSPATH"/"$2"/AM-updater; then
			echo " ERROR: \"$AMCLI\" cannot manage updates for \"$2\""
		elif ! grep -q "api.github.com" "$APPSPATH"/"$2"/AM-updater; then
			echo " ERROR: \"$2\" source is not on Github"
		elif ! grep -q "/releases | " "$APPSPATH"/"$2"/AM-updater; then
			echo " ERROR: \"$2\" does not redirect to a generic \"releases\""
		else
			sed -i 's#/releases | #/releases/latest | #g' "$APPSPATH"/"$2"/AM-updater
			"$AMCLIPATH" -u "$2"
		fi
		;;
	esac
}

#############################################
# FUNCTION TO HANDLE ALL THE EXTERNAL MODULES
#############################################

function _use_module() {
	# Test if module exists
	if ! test -f "$AMPATH/modules/$MODULE"; then
		wget -q "$AMREPO/modules/$MODULE" -O "$AMPATH/modules/$MODULE"
		chmod a+x "$AMPATH/modules/$MODULE"
	fi
	# Source module
	source "$AMPATH/modules/$MODULE" "$@"
}

_am_security_check
_am_databases_check "$@"

case "$1" in
	'')
		echo -e "\n USAGE: '$AMCLI [option]'\n        '$AMCLI [option] [argument]'\n\n Run the '$AMCLI -h' command to find out more\n"
		;;
	# OPTIONS BASED ON EXTERNAL MODULES
	'-a'|'about'|\
	'-l'|'list'|\
	'-q'|'query')
		MODULE="database.am"
		_use_module "$@"
		;;
	'-b'|'backup'|\
	'-o'|'overwrite'|\
	'-R'|'-r'|'remove'|\
	'--rollback'|'downgrade'|\
	'lock'|'unlock'|\
	'nolibfuse')
		MODULE="management.am"
		_use_module "$@"
		;;
	'-d'|'download')
		MODULE="download.am"
		_online_check
		_use_module "$@"
		;;
	'-f'|'files')
		MODULE="files.am"
		_use_module "$@"
		_betatester_message_on
		;;
	'-h'|'help')
		MODULE="help.am"
		_if_appman_mode_enabled
		_use_module "$@"
		;;
	'-C'|'--config'|\
	'-H'|'--home'|\
	'--sandbox'|\
	'--disable-sandbox')
		MODULE="sandboxes.am"
		_use_module "$@"
		;;
	'-i'|'install'|\
	'-e'|'extra')
		MODULE="install.am"
		_online_check
		_if_appman_mode_enabled
		_use_module "$@"
		;;
	'-t'|'template')
		MODULE="template.am"
		_online_check
		_use_module "$@"
		;;
	'--launcher')
		MODULE="launcher.am"
		_use_module "$@"
		;;
	'neodb'|\
	'apikey'|\
	'newrepo')
		MODULE="devtools.am"
		_online_check
		_use_module "$@"
		;;
	# INBUILT OPTIONS
	'--devmode-disable')
		_use_betatester_off
		;;
	'--devmode-enable')
		_use_betatester_on
		_betatester_message_on
		;;
	'--disable-completion')
		_use_disable-completion
		;;
	'--enable-completion')
		_use_enable_completion
		;;
	'--force-latest')
		_online_check
		_use_force_latest "$@"
		;;
	'--system')
		_back_to_am
		;;
	'--user'|'appman')
		_use_appman
		;;
	'clean'|'-c')
		_use_clean
		;;
	'sync'|'-s')
		_online_check
		_betatester_message_on
		_use_sync
		;;
	'update'|'-u'|'-U')
		_online_check
		_use_update "$@"
		;;
	'version'|'-v'|'--version')
		echo "$AMVERSION"
		;;
	*)
		exec "$AMCLIPATH"
		;;
esac

# vim:tabstop=4:shiftwidth=4:expandtab
